class PSPawn extends UDKPawn
	abstract;

/** The pawn's light environment */
var DynamicLightEnvironmentComponent LightEnvironment;

var bool bDead;
var bool bJustHeardNoise;
var bool bCanPlaySound;

var GameSkelCtrl_Recoil RecoilSkelControlRightArm;
var GameSkelCtrl_Recoil RecoilSkelControlLeftArm;

enum EAnimations
{
	// Didrik animations
	ANIM_Didrik_AFK,
	ANIM_Didrik_Lift_Heavy,
	ANIM_Didrik_Unlift_Heavy,
	ANIM_Didrik_Throw_Heavy,
	ANIM_Didrik_Save_Pistol,
	ANIM_Didrik_Hold_Pistol,
	ANIM_Didrik_Save_Pistol_Cover,
	ANIM_Didrik_Hold_Pistol_Cover,
	ANIM_Didrik_Reload_Rifle,
	ANIM_Didrik_Reload_Pistol,
	ANIM_Didrik_Reload_Rifle_Cover,
	ANIM_Didrik_Reload_Pistol_Cover,
	ANIM_Didrik_Melee_Unarmed,
	ANIM_Didrik_Melee_Pistol,
	ANIM_Didrik_Melee_Rifle,

	// Enemy animations
	ANIM_Enemy_Melee_Pistol,
	ANIM_Enemy_Melee_Rifle,
	ANIM_Enemy_Reload_Rifle,
	ANIM_Enemy_Reload_Pistol,
	ANIM_Enemy_Look_Around_Fast_Rifle,
	ANIM_Enemy_Look_Around_Fast_Pistol,
	ANIM_Enemy_Look_Around_Slow_Rifle,
	ANIM_Enemy_Look_Around_Slow_Pistol,
	ANIM_Enemy_Check_Floor_Rifle,
	ANIM_Enemy_Check_Floor_Pistol
};

enum ESoundType
{
	SOUND_WeaponFire,
	SOUND_BulletImpact,
	SOUND_SilencedBulletImpact,
	SOUND_Footstep,
	SOUND_FightAlert,
	SOUND_InvisibleTransition
};

/*var float helth;
var float Health;
*/

// Flags for animation tree control
enum EPosture
{
	POSTURE_Walking,
	POSTURE_Running,
	POSTURE_Crouching,
	POSTURE_Covered_Stand,
	POSTURE_Covered_Crouch,
	POSTURE_Carrying,
	POSTURE_Checking,
	POSTURE_Hacking
};

enum EWeapon
{
	WEAPON_Unarmed,
	WEAPON_Rifle,
	WEAPON_Pistol,
	WEAPON_Shotgun
};

var AnimNodeSlot FullBodyAnimNodeSlot;      // AnimNodeSlot for full body animations, such as throwing objects
var AnimNodeSlot TopHalfBodyAnimNodeSlot;   // AnimNodeSlot for top half body animations, such as weapon switching or reloading
var AnimNodeAimOffset AimOffsetNode;

var EPosture CurrentPosture;
var EWeapon CurrentWeapon;

// Recoil control
simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp)
{
	super.PostInitAnimTree(SkelComp);

	// If the pawn is a security camera, avoid initializing all character stuff
	if(PSSecurityCamera(self) != none)
	{
		return;
	}

	if(SkelComp == Mesh)
	{		
		AimOffsetNode = AnimNodeAimOffset(SkelComp.FindAnimNode('AimNode'));
		FullBodyAnimNodeSlot = AnimNodeSlot(SkelComp.FindAnimNode('FullBodySlot'));
		TopHalfBodyAnimNodeSlot = AnimNodeSlot(SkelComp.FindAnimNode('TopHalfSlot'));
		AimOffsetNode.SetActiveProfileByName('Armed');

		RecoilSkelControlRightArm = GameSkelCtrl_Recoil(Mesh.FindSkelControl('RightArmRecoil'));
		RecoilSkelControlLeftArm = GameSkelCtrl_Recoil(Mesh.FindSkelControl('LeftArmRecoil'));
	}
}

simulated event Destroyed()
{
	super.Destroyed();

	RecoilSkelControlRightArm = none;
	RecoilSkelControlLeftArm = none;
}

function PlayRecoil(EWeapon w)
{
	EnableRecoil();

	if(w == EWeapon.WEAPON_Rifle)
	{
		SetTimer(0.1, true, 'EnableRecoil');	
	}
	else if(w == EWeapon.WEAPON_Pistol)
	{
		SetTimer(0.3, true, 'EnableRecoil');
	}
}

function EnableRecoil()
{
    if (RecoilSkelControlRightArm != None)
	{
		RecoilSkelControlRightArm.bPlayRecoil = true;
	}

	if (RecoilSkelControlLeftArm != None)
	{
		RecoilSkelControlLeftArm.bPlayRecoil = true;
	}
}

function StopRecoil()
{
	ClearTimer('EnableRecoil');
}


/**
 * Pawn reload active weapon
 */
simulated function ReloadWeapon()
{
	//WorldInfo.Game.Broadcast(self,"ReloadWeapon");
	if( bNoWeaponFIring )
	{
		return;
	}

	if( Weapon != None )
	{
		//WorldInfo.Game.Broadcast(self,"ReloadWeapon weapon");
		PSWeapon(Weapon).ReloadMag(); //.StartFire(FireModeNum);
		PSWeapon(Weapon).RecalcularVisor();
		//WorldInfo.Game.Broadcast(self,"ReloadWeapon weapon 2");
	}
}


function BroadcastNoise(Vector NoiseLocation, ESoundType SoundType)
{
	local PSPawnEnemyRandom currentPawn;
	local PSAIControllerBase currentController;
	local float distance;

	foreach WorldInfo.AllPawns(class'PSPawnEnemyRandom', currentPawn)
	{
		// Only consider living pawns
		if(!(currentPawn.bDead) && !bJustHeardNoise)
		{
			distance = VSize(currentPawn.Location - NoiseLocation);
			currentController = PSAIControllerBase(currentPawn.Controller);

			// Only consider sounds if not already with a visible target
			if(currentController.IsInState('Idle') ||
				currentController.IsInState('Hacking') ||
				currentController.IsInState('Patrol') ||
				currentController.IsInState('Upset') ||
				currentController.IsInState('Check') ||
				currentController.IsInState('GoToCheck') ||
				currentController.IsInState('BackToStart') )
			{
				if( ((SoundType == ESoundType.SOUND_WeaponFire) &&
					(distance <= currentPawn.WeaponFireHearingThreshold)) ||
					((SoundType == ESoundType.SOUND_BulletImpact) &&
					(distance <= currentPawn.BulletImpactHearingThreshold)) ||
					((SoundType == ESoundType.SOUND_FightAlert) &&
					(distance <= currentPawn.FightAlertHearingThreshold)) )
				{
					currentPawn.bJustHeardNoise = true;
					SetTimer(3.0, false, 'ResetJustHeardNoise');

					if(currentController.WantToCover())
					{
						currentController.GotoState('TakeCover');
					}
					else
					{
						currentController.GotoState('Evade');
					}	
				}
				// If footsteps, silenced weapon bullets impact, or the invisible transition are heard, turn around to check
				else if ( ((SoundType == ESoundType.SOUND_Footstep) &&
					(distance <= currentPawn.FootstepHearingThreshold)) || 
					((SoundType == ESoundType.SOUND_SilencedBulletImpact) &&
					(distance <= currentPawn.BulletImpactHearingThreshold)) ||
					((SoundType == ESoundType.SOUND_InvisibleTransition) &&
					(distance <= currentPawn.InvisibleTransitionHearingThreshold)))
				{
					currentController.HeardSound(NoiseLocation);

					currentPawn.bJustHeardNoise = true;
					SetTimer(1.0, false, 'ResetJustHeardNoise');
				}				
			}	
		}	
	}
}

function ResetJustHeardNoise()
{
	bJustHeardNoise = false;
}

function PlayAnim(EAnimations AnimName)
{
	switch(AnimName)
	{
	// Didrik animations
	case ANIM_Didrik_AFK:
		FullBodyAnimNodeSlot.PlayCustomAnim('idle2_unarmed', 1.0, 0.25, 0.25, false);
		break;
	case ANIM_Didrik_Lift_Heavy:
		FullBodyAnimNodeSlot.PlayCustomAnim('lift_object_strength', 1.0, 0.25, 0.25, false);
		break;
	case ANIM_Didrik_Unlift_Heavy:
		FullBodyAnimNodeSlot.PlayCustomAnim('unlift_object_strength', 1.0, 0.25, 0.25, false);
		break;
	case ANIM_Didrik_Throw_Heavy:
		FullBodyAnimNodeSlot.PlayCustomAnim('throw_strength', 1.0, 0.25, 0.25, false);
		break;
	case ANIM_Didrik_Save_Pistol:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('save_pistol', 1.0, 0.25, 0.25, false);
		break;
	case ANIM_Didrik_Hold_Pistol:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('save_pistol', -1.0, 0.25, 0.25, false);
		break;
	case ANIM_Didrik_Save_Pistol_Cover:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('save_pistol_cover', 1.0, 0.25, 0.25, false);
		break;
	case ANIM_Didrik_Hold_Pistol_Cover:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('save_pistol_cover', -1.0, 0.25, 0.25, false);
		break;
	case ANIM_Didrik_Reload_Rifle:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('reload_rifle', 1.25, 0.25, 0.25, false);
		break;
	case ANIM_Didrik_Reload_Pistol:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('reload_pistol', 1.25, 0.25, 0.25, false);
		break;
	case ANIM_Didrik_Reload_Rifle_Cover:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('reload_cover_rifle', 1.25, 0.25, 0.25, false);
		break;
	case ANIM_Didrik_Reload_Pistol_Cover:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('reload_cover_pistol', 1.25, 0.25, 0.25, false);
		break;
	case ANIM_Didrik_Melee_Unarmed:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('melee_unarmed', 1.0, 0.25, 0.25, false);
		break;
	case ANIM_Didrik_Melee_Pistol:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('melee_pistol_fast', 0.75, 0.25, 0.25, false);
		break;
	case ANIM_Didrik_Melee_Rifle:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('melee_rifle_Fast', 0.75, 0.25, 0.25, false);
		break;

	// Enemies animations
	case ANIM_Enemy_Melee_Pistol:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('melee_pistol_fast', 0.75, 0.25, 0.25, false);
		break;
	case ANIM_Enemy_Melee_Rifle:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('melee_rifle_Fast', 0.75, 0.25, 0.25, false);
		break;
	case ANIM_Enemy_Reload_Rifle:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('reload_rifle', 1.25, 0.25, 0.25, false);
		break;
	case ANIM_Enemy_Reload_Pistol:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('reload_pistol', 1.25, 0.25, 0.25, false);
		break;
	case ANIM_Enemy_Look_Around_Fast_Rifle:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('look_around_fast_rifle_ready', 1.0, 0.25, 0.25, false);
		break;
	case ANIM_Enemy_Look_Around_Fast_Pistol:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('look_around_fast_pistol_ready', 1.0, 0.25, 0.25, false);
		break;
	case ANIM_Enemy_Look_Around_Slow_Rifle:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('look_around_slow_rifle_ready', 1.0, 0.25, 0.25, false);
		break;
	case ANIM_Enemy_Look_Around_Slow_Pistol:
		TopHalfBodyAnimNodeSlot.PlayCustomAnim('look_around_slow_pistol_ready', 1.0, 0.25, 0.25, false);
		break;
	case ANIM_Enemy_Check_Floor_Rifle:
		FullBodyAnimNodeSlot.PlayCustomAnim('check_floor_rifle_ready', 1.25, 0.25, 0.25, false);
		break;
	case ANIM_Enemy_Check_Floor_Pistol:
		FullBodyAnimNodeSlot.PlayCustomAnim('check_floor_pistol_ready', 1.25, 0.25, 0.25, false);
		break;
	}
}


function bool Died(Controller Killer, class<DamageType> DamageType, vector HitLocation)
{
	local SeqAct_Latent Action;

	// ensure a valid damagetype
	if ( damageType == None )
	{
		damageType = class'DamageType';
	}
	// if already destroyed or level transition is occuring then ignore
	if ( bDeleteMe || WorldInfo.Game == None || WorldInfo.Game.bLevelChange )
	{
		return FALSE;
	}

	// activate death events
	if( default.KismetDeathDelayTime > 0 )
	{
		DelayTriggerDeath();
	}
	else
	{
		TriggerEventClass(class'SeqEvent_Death', self, 0);
	}

	KismetDeathDelayTime = default.KismetDeathDelayTime + WorldInfo.TimeSeconds;

	// and abort any latent actions
	foreach LatentActions(Action)
	{
		Action.AbortFor(self);
	}
	LatentActions.Length = 0;

	if (Weapon != None)
	{
		if(Weapon.IsA('PSWeaponFusil')) PSWeaponFusil(Weapon).StopMuzzleFlashEffect();
		Weapon.HolderDied();
		ThrowWeaponOnDeath();
	}

	PlayDying(DamageType, HitLocation);
	return true;
}


function TossInventory(Inventory Inv, optional vector ForceVelocity)
{
	local vector	POVLoc, TossVel;
	local rotator	POVRot;
	local Vector	X,Y,Z;

	if ( ForceVelocity != vect(0,0,0) )
	{
		TossVel = ForceVelocity;
	}
	else
	{
		GetActorEyesViewPoint(POVLoc, POVRot);
		TossVel = Vector(POVRot);
		TossVel = TossVel * ((Velocity Dot TossVel) + 500);
	}

	GetAxes(Rotation, X, Y, Z);
	Inv.DropFrom(Location, TossVel);
}


simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
{
	Mesh.SetRBChannel(RBCC_Pawn);
	Mesh.SetRBCollidesWithChannel(RBCC_Default, true);
	Mesh.SetRBCollidesWithChannel(RBCC_Pawn, true);
	Mesh.SetRBCollidesWithChannel(RBCC_Vehicle, true);
	Mesh.SetRBCollidesWithChannel(RBCC_Untitled3, false);
	Mesh.SetRBCollidesWithChannel(RBCC_BlockingVolume, true);
	Mesh.MinDistFactorForKinematicUpdate = 0.0;
	Mesh.ForceSkelUpdate();
	Mesh.SetTickGroup(TG_PostAsyncWork);
	CollisionComponent = Mesh;
	CylinderComponent.SetActorCollision(false, false);
	Mesh.SetActorCollision(false, false);
	Mesh.SetTraceBlocking(true, true);
	Mesh.SetBlockRigidBody(true);
	SetPhysics( PHYS_RigidBody );
	Mesh.PhysicsWeight = 1.f;
	Mesh.PhysicsAssetInstance.SetAllBodiesFixed(false);
	Mesh.bUpdateKinematicBonesFromAnimation = false;
	Mesh.WakeRigidBody();
		
	bReplicateMovement = false;
	bTearOff = true;
	Velocity += TearOffMomentum;
	InitRagdoll();
	SetDyingPhysics();
	bPlayedDeath = true;
}

simulated event PlayFootStepSound(int FootDown)
{
	local name MaterialBelowFeet;

	// Avoid playing step sounds if dead (UDK bug?)
	if(bDead)
	{
		return;
	}

	MaterialBelowFeet = GetMaterialBelowFeet();

	// If walking over a metal prop play a metal footstep sound
	if( (MaterialBelowFeet != '') && 
		((MaterialBelowFeet == 'M_floor_grate_192x192') ||
		 (MaterialBelowFeet == 'M_girder_192_2')) )
	{
		// Left foot
		if (FootDown == 0)
		{
			PlaySound(SoundCue'PS_sounds.FootSteps.AU_Run_Footstep_Metal_Left_Cue', false, true, , , true);
		
		}
		// Right Foot
		else if (FootDown == 1)
		{
			PlaySound(SoundCue'PS_sounds.FootSteps.AU_Run_Footstep_Metal_Right_Cue', false, true, , , true);	
		}
	}
	// No metal surface --> play standard footstep sound
	else
	{
		// Left foot
		if (FootDown == 0)
		{
			if(CurrentPosture == EPosture.POSTURE_Running)
			{
				PlaySound(SoundCue'PS_sounds.FootSteps.AU_Run_Footstep_Left_Cue', false, true, , , true);
			}
			else if( (CurrentPosture == EPosture.POSTURE_Walking) ||
				(CurrentPosture == EPosture.POSTURE_Covered_Stand) ||
				(CurrentPosture == EPosture.POSTURE_Carrying) )
			{
				PlaySound(SoundCue'PS_sounds.FootSteps.AU_Walk_Footstep_Left_Cue', false, true, , , true);
			}
		
		}
		// Right Foot
		else if (FootDown == 1)
		{
			if(CurrentPosture == EPosture.POSTURE_Running)
			{
				PlaySound(SoundCue'PS_sounds.FootSteps.AU_Run_Footstep_Right_Cue', false, true, , , true);			
			}
			else if( (CurrentPosture == EPosture.POSTURE_Walking) ||
				(CurrentPosture == EPosture.POSTURE_Covered_Stand) ||
				(CurrentPosture == EPosture.POSTURE_Carrying) )
			{
				PlaySound(SoundCue'PS_sounds.FootSteps.AU_Walk_Footstep_Right_Cue', false, true, , , true);
			}
		}
	}

	
	// Broadcast if the pawn is running or walking (not taking care of being sneaky),
	// but only if the pawn is Didrik (player)
	if( (PSPawnDidrik(self) != none) && 
		( (CurrentPosture == EPosture.POSTURE_Walking) || 
		  (CurrentPosture == EPosture.POSTURE_Running) ||
		  (CurrentPosture == EPosture.POSTURE_Carrying)) )
	{
		BroadcastNoise(Location, ESoundType.SOUND_Footstep);
	}
}

simulated function name GetMaterialBelowFeet()
{
	local vector HitLocation, HitNormal;
	local TraceHitInfo HitInfo;
	local float TraceDist;

	TraceDist = 1.5 * GetCollisionHeight();

	Trace(HitLocation, HitNormal, Location - TraceDist*vect(0,0,1), Location, false,, HitInfo, TRACEFLAG_PhysicsVolumes);
	
	if(MeshComponent(HitInfo.HitComponent) != none)
	{			 
		return MeshComponent(HitInfo.HitComponent).GetMaterial(0).Name;
	} 	
	else
	{
		return '';
	}
}

function ClearPlaySound()
{
	bCanPlaySound = true;
}

DefaultProperties
{
	Begin Object Class=DynamicLightEnvironmentComponent Name=MyLightEnvironment
		MinTimeBetweenFullUpdates=0.2
		bSynthesizeSHLight=TRUE
		bIsCharacterLightEnvironment=TRUE
		bUseBooleanEnvironmentShadowing=FALSE
	End Object
	Components.Add(MyLightEnvironment)
	LightEnvironment=MyLightEnvironment

	InventoryManagerClass=class'PSInventoryManager'

	Health = 100
	HealthMax = 100

	bCollideActors=true
	bCanStepUpOn=false
	bDead = false
	bJustHeardNoise=false
	bCanPlaySound=true
	bCanCrouch=true	

	// Limit the look up and down of the pawn
	ViewPitchMin=-7500 
	ViewPitchMax=7500
}